package org.my.workflow.persistence;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.my.workflow.WorkflowContext;
import org.my.workflow.WorkflowException;
import org.my.workflow.WorkflowPersistence;
import org.my.workflow.element.StepElement;
import org.my.workflow.element.TargetStepElement;

/**
 * 实现在内存中持久化 Workflow
 *
 * @author <a href="mailto:yang_y@sysnet.com.cn">Young Yang</a>
 */
public class MemoryWorkflowPersistence implements WorkflowPersistence {

    private Map<String, WorkflowEntity> entities = new HashMap<String, WorkflowEntity>();

    private Map<String, Map<String, StepEntity>> currentWorkflowSteps = new HashMap<String, Map<String, StepEntity>>();

    /**
     * TODO: Map 保存不合适，因为一个Step可能执行多次
     */
    private Map<String, List<StepEntity>> historyWorkflowSteps = new HashMap<String, List<StepEntity>>();

    /**
     * 等待 Join 的 Step
     */
    private List<StepEntity> queuedStepEntities = new ArrayList<StepEntity>();

    public WorkflowEntity newWorkflowEntity(String wfId) {
        WorkflowEntity wfEntity = new WorkflowEntity();
        wfEntity.setId(wfId);
        entities.put(wfId, wfEntity);
        return wfEntity;
    }

    public void updateWorkflowEntity(WorkflowEntity wfEntity) {
        entities.put(wfEntity.getId(), wfEntity);
    }

    public WorkflowEntity getWorkflowEntity(String wfId) {
        return entities.get(wfId);
    }

    public Collection<StepEntity> nextSteps(WorkflowContext wfContext, String currentStepElementId, TargetStepElement... targetStepElements) {
        String wfId = wfContext.getWorkflowId();
        Map<String, StepEntity> currentSteps = currentWorkflowSteps.get(wfId);
        if (currentSteps == null) {
            currentSteps = new HashMap<String, StepEntity>();
            currentWorkflowSteps.put(wfId, currentSteps);
        }

        StepEntity thisStepEntity = null;
        if(currentSteps.containsKey(currentStepElementId)){
            thisStepEntity = currentSteps.get(currentStepElementId);
        }

        // workflow 是否 completed
        boolean completed = true;
        if(targetStepElements.length == 0) { // no target ???
            throw new WorkflowException("No fit target step element!");
        }
        else if(targetStepElements.length == 1) { //TODO: simple transition or join
            TargetStepElement targetStepElement = targetStepElements[0];
            StepEntity stepEntity = new StepEntity(UUID.randomUUID().toString(), wfId, targetStepElement.getStepId());
            stepEntity.setStartDate(new Date());
            stepEntity.setCaller(wfContext.getCaller());
            // update entity
            updateStep(stepEntity);

            // 将TargetStep作为 currentStep，先验证TargetStep的Condition
            // 如果Target Step Condition 通过，则将 Target Step 作为 current step
            StepElement stepElement = wfContext.getStepElement(targetStepElement.getStepId());
            if(stepElement.checkCondition(wfContext)){
                // finish current step
                finishStep(thisStepEntity);
                // remove current step from currentSteps
                currentSteps.remove(currentStepElementId);
                // set target step element as current step
                currentSteps.put(targetStepElement.getStepId(), stepEntity);

                // 无需清除其它等待合并的 Step，因为 JoinCondition 已经做了
                
            }
            else { //放置在 queuedStepEntities中，等待合并
                queueStep(thisStepEntity);
                // remove current step from currentSteps
                currentSteps.remove(currentStepElementId);
            }

            // 判断 targetStepElement 的 status，如果是 Finished，说明流程结束
            if(!targetStepElement.getStatus().equalsIgnoreCase("Finished")) {
                completed = false;
            }

        }
        else { // split
            // should not be completed.
            completed = false;
            for (TargetStepElement targetStepElement : targetStepElements) {
                StepEntity stepEntity = new StepEntity(UUID.randomUUID().toString(), wfId, targetStepElement.getStepId());
                stepEntity.setStartDate(new Date());
                stepEntity.setCaller(wfContext.getCaller());
                // update entity
                updateStep(stepEntity);

                // 将TargetStep作为 currentStep，先验证TargetStep的Condition
                // 如果Target Step Condition 通过，则将 Target Step 作为 current step
                StepElement stepElement = wfContext.getStepElement(targetStepElement.getStepId());
                if(stepElement.checkCondition(wfContext)){
                    currentSteps.put(targetStepElement.getStepId(), stepEntity);

                }
                else { //无法分支，抛出异常
                    throw new WorkflowException("Can not enter step: " + stepElement.getId());
                }

            }

        }

        if(completed) {
            // mark workflow completed.
            getWorkflowEntity(wfId).setStatus(WorkflowStatus.COMPLETED);
        }
        else {
            getWorkflowEntity(wfId).setStatus(WorkflowStatus.ACTIVATED);
        }
        // update workflow entity
        updateWorkflowEntity(getWorkflowEntity(wfId));
        return currentSteps.values();
    }

    public Collection<StepEntity> getCurrentSteps(String wfId) {
        if(!currentWorkflowSteps.containsKey(wfId)) {
            return Collections.emptyList();
        }
        return currentWorkflowSteps.get(wfId).values();
    }

    public void updateStep(StepEntity stepEntity) {

    }

    public void finishStep(StepEntity stepEntity) {
        stepEntity.setFinishDate(new Date());
        List<StepEntity> historyStepEntities = historyWorkflowSteps.get(stepEntity.getWorkflowId());
        if (historyStepEntities == null) {
            historyStepEntities = new ArrayList<StepEntity>();
            historyWorkflowSteps.put(stepEntity.getWorkflowId(), historyStepEntities);
        }
        historyStepEntities.add(stepEntity);
    }

    //TODO: finish queue step
    public void queueStep(StepEntity stepEntity) {
        stepEntity.setFinishDate(new Date());
        List<StepEntity> historyStepEntities = historyWorkflowSteps.get(stepEntity.getWorkflowId());
        if (historyStepEntities == null) {
            historyStepEntities = new ArrayList<StepEntity>();
            historyWorkflowSteps.put(stepEntity.getWorkflowId(), historyStepEntities);
        }
        historyStepEntities.add(stepEntity);
    }

    public List<StepEntity> getHistorySteps(String wfId){
        if (!historyWorkflowSteps.containsKey(wfId)) {
            return Collections.emptyList();
        }
        return Collections.unmodifiableList(historyWorkflowSteps.get(wfId));
    }

    /**
     * get StepEntity by StepEntity id
     * @param wfId
     * @param stepEntityId
     * @return
     */
    public StepEntity getStep(String wfId, String stepEntityId) {
        if (!historyWorkflowSteps.containsKey(wfId)) {
            return null;
        }

        List<StepEntity> historyStepEntities = historyWorkflowSteps.get(wfId);
        StepEntity stepEntity = null;
        for(StepEntity _stepEntity : historyStepEntities){
            if(_stepEntity.getId().equals(stepEntityId)){
                stepEntity = _stepEntity;
                break;
            }
        }
        return stepEntity;
    }

    public List<StepEntity> getQueuedSteps(){
        return Collections.unmodifiableList(queuedStepEntities);
    }

    /**
     * 删除 QueueStep，由 JoinCondition 调用
     * @param stepId
     */
    public boolean removeQueuedStep(String stepId) {
        boolean removed = false;
        for(Iterator<StepEntity> it = queuedStepEntities.iterator() ; it.hasNext();){
            StepEntity stepEntity = it.next();
            if(stepEntity.getId().equals(stepId)) {
                removed = true;
                it.remove();
            }
        }
        return removed;
    }

    public static void main(String[] args) {

    }
}
